#define DEBUG
//#define DEBUG_INPUT

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Windows.Forms;
using Odin.Base.IO;
using Odin.Base;

namespace Odin {
    public class OdinEngine {
        public static Window Window;

        private static Stopwatch timer = new Stopwatch();
        private static bool running = true;
        private static int ticks = 0, tickCount = 0, frames = 0;
        private static KeyboardState oldState;

        static OdinEngine() {
            // Start up the window thread
            new Thread(new ThreadStart(Start)).Start();
        }

        private static void Initialize() {
            // Set up Input
            oldState = Input.getKeyboardState();

            // Initialize Input
            Input.Initialize();
        }

        private static void Start() {
            // LOG: Main Window Thread started
            Odin.Base.IO.Console.Log("System", "Main Window Thread started");
#if (DEBUG)
            System.Console.WriteLine("LOG: Main Window Thread started");
#endif
            // Start the main thread
            new Thread(new ThreadStart(Run)).Start();
            // LOG: Main Engine Thread started
            Odin.Base.IO.Console.Log("System", "Main Engine Thread started");
#if (DEBUG)
            System.Console.WriteLine("LOG: Main Engine Thread started");
#endif

            // Create a new window
            Window = new Window(Width: 800, Height: 600);
            Window.Show();

            // Start intercepting key presses
            Application.AddMessageFilter(new Input.Interception());

            // Initialize the engine
            Initialize();

            // Start the message loop
            Application.Run();
        }

        public static void Stop() {
            // Stop the main loop before closing the application
            running = false;

            // LOG: Stopping Engine
            Odin.Base.IO.Console.Log("System", "Stopping Engine");

            // Save the log
            Odin.Base.IO.Console.SaveLog();

            // Exit
            Application.Exit();
        }

        private static void Run() {
            double total = 0;
            timer.Start();
            long last = timer.ElapsedMilliseconds;
            long displayTimer = timer.ElapsedMilliseconds;
            while (running) {
                long now = timer.ElapsedMilliseconds;
                total += (now - last) / (1000.0 / 60.0);
                last = now;
                while (total > 1) {
                    Tick();
                    Draw();
                    ticks++;
                    total -= 1;
                }
                if (timer.ElapsedMilliseconds - displayTimer > 1000) {
                    displayTimer += 1000;
                    //Debug.WriteLine(ticks + " ticks, " + tickCount + "  tickCount, " + frames + " fps");
                    ticks = 0;
                    frames = 0;
                }
            }
        }

        private static void Tick() {
            tickCount++;
            UpdateInput();
        }

        private static void UpdateInput() {
            KeyboardState currentState = Input.getKeyboardState();

            // Check modifier status
            byte pressedMods = 0;
            for (int i = 0; i < Keybinding.Mods.Length; i++) {
                if (currentState.IsKeyDown(Keybinding.Mods[i])) {
                    pressedMods += (byte)(1 << i);
                }
            }

            foreach (Keybinding bind in Input.Keybindings) {
                if (currentState.IsKeyDown(bind.Key)) {
                    if (!oldState.IsKeyDown(bind.Key)) {
                        // If the modifiers that are pressed, are equal to what should be pressed
                        if (pressedMods == bind.Modifiers) {
                            // Key has just been pressed
#if (DEBUG && DEBUG_INPUT)
                            System.Console.WriteLine("Key " + bind.Key.ToString() + " has been pressed");
#endif
                            // Execute related action
                            if (bind.Pressed != null) {
                                bind.Pressed();
                            }
                        }
                    } else {
                        if (pressedMods == bind.Modifiers) {
                            // Key is being held down
#if (DEBUG && DEBUG_INPUT)
                            System.Console.WriteLine("Key " + bind.Key.ToString() + " is being held");
#endif
                            // Execute related action
                            if (bind.Held != null) {
                                bind.Held();
                            }
                        }
                    }
                }
                if (oldState.IsKeyDown(bind.Key)) {
                    if (!currentState.IsKeyDown(bind.Key)) {
                        if (pressedMods == bind.Modifiers) {
                            // Key has just been released
#if (DEBUG && DEBUG_INPUT)
                            System.Console.WriteLine("Key " + bind.Key.ToString() + " has been released");
#endif
                            // Execute related action
                            if (bind.Released != null) {
                                bind.Released();
                            }
                        }

                    }
                }
            }

            oldState = currentState;
        }

        private static void Draw() {
            if (Window != null) {
                try {
                    Window.Draw();
                } catch (Exception e) {
                    Debug.WriteLine(e.InnerException);
                    Debug.WriteLine(e.Message);
                }
            }
        }

        static void Main(string[] args) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
        }
    }
}

